package com.NetSunWeiQin.chapter04.nonblock;

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.util.*;

/**
 * nio  非阻塞响应
 */
public class NioEchoServer {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel ;
    private int port = 8000;
    private Charset charset = StandardCharsets.UTF_8;

    private NioEchoServer() throws IOException {
        selector = Selector.open();
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().setReuseAddress(true);
        serverSocketChannel.configureBlocking(false);  //设置为非阻塞模式 , 这个时候 accept() 会直接返回 ;  if 没有连接, 返回的是 null ;
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        System.out.println("INFO 服务器已经启动.....................");
    }

    private void service() throws IOException {
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (selector.select() > 0) {
            Set readyKeys = selector.selectedKeys();
            Iterator it = readyKeys.iterator();
            while (it.hasNext()) {
                SelectionKey key = null;
                try {
                    key = (SelectionKey) it.next();
                    it.remove();

                    if (key.isAcceptable()) {
                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        System.out.println("INFO 接收到客户连接，来自:" + socketChannel.socket().getInetAddress() + ":" + socketChannel.socket().getPort());
                        socketChannel.configureBlocking(false);
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        /*当 有新连接今来的时候, 必须监听  读写的 selectionKey , 来准备读写  */
                        socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, buffer);
                    }
                    if (key.isReadable()) {    //有data就会来这里
                        receive(key);
                    }
                    if (key.isWritable()) {   //能写就会走这里
                        send(key);
                    }
                } catch (IOException e) {
                    System.out.println("ERROR " + e.getMessage());
                    e.printStackTrace();
                    try {
                        if (key != null) {
                            key.cancel();
                            key.channel().close();
                        }
                    } catch (Exception ex) {
                        e.printStackTrace();
                        System.out.println("ERROR " + ex.getMessage());
                    }
                }
            }
        }
    }


    private void receive(SelectionKey key) throws IOException {
        ByteBuffer buffer = (ByteBuffer) key.attachment();

        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer readBuff = ByteBuffer.allocate(32);
        socketChannel.read(readBuff);
        readBuff.flip();

        buffer.limit(buffer.capacity());
        buffer.put(readBuff);
    }



    private void send(SelectionKey key) throws IOException {
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        SocketChannel socketChannel = (SocketChannel) key.channel();
        buffer.flip();  //把极限位置为0
        String data = decode(buffer);
        if (!data.contains("\r\n")) {
            return;
        }
        String outputData = data.substring(0, data.indexOf("\n") + 1);
        System.out.print(outputData);
        ByteBuffer outputBuffer = encode("echo:" + outputData);
        while (outputBuffer.hasRemaining()) {
            socketChannel.write(outputBuffer);
        }
        ByteBuffer temp = encode(outputData);
        buffer.position(temp.limit());
        buffer.compact();

        if ("bye\r\n".equals(outputData)) {
            key.cancel();
            socketChannel.close();
            System.out.println("WARN  关闭与客户的连接");
        }
    }

    private String decode(ByteBuffer buffer) {  //解码
        CharBuffer charBuffer = charset.decode(buffer);
        return charBuffer.toString();
    }

    private ByteBuffer encode(String str) {  //编码
        return charset.encode(str);
    }

    public static void main(String args[]) throws Exception {
        NioEchoServer server = new NioEchoServer();
        server.service();
    }
}



